package com.ttsf.compiler

import com.google.auto.service.AutoService
import com.squareup.javapoet.*
import com.ttsf.api.ARouter
import com.ttsf.api.bean.RouterBean
import com.ttsf.compiler.utils.ProcessorConfig
import com.ttsf.compiler.utils.ProcessorUtils
import java.io.IOException
import javax.annotation.processing.*
import javax.lang.model.SourceVersion
import javax.lang.model.element.Modifier
import javax.lang.model.element.TypeElement
import javax.lang.model.util.Elements
import javax.lang.model.util.Types
import javax.tools.Diagnostic

/**
 * @Description:
 * @Created by zhang on  2020/11/16 9:44
 */

@AutoService(value = [Processor::class])
@SupportedAnnotationTypes(value = ["com.ttsf.api.ARouter"])
@SupportedSourceVersion(value = SourceVersion.RELEASE_8)
// 注解处理器接收的参数
@SupportedOptions(ProcessorConfig.OPTIONS, ProcessorConfig.APT_PACKAGE)
class ARouterProcessor : AbstractProcessor() {

    private var elementTool: Elements? = null
    private var typeTool: Types? = null
    private var messager: Messager? = null
    private var filer: Filer? = null

    private var options // 各个模块传递过来的模块名 例如：app order personal
            : String? = null
    private var aptPackage // 各个模块传递过来的目录 用于统一存放 apt生成的文件
            : String? = null


    // 仓库一 Path  缓存一
    // Map<"personal", List<RouterBean>>
    private var mAllPathMap: MutableMap<String?, ArrayList<RouterBean>> = mutableMapOf() // 目前是一个


    // 仓库二 Group 缓存二
    // Map<"personal", "ARouter$$Path$$personal.class">
    private val mAllGroupMap: MutableMap<String, String> = mutableMapOf()


    override fun init(processingEnv: ProcessingEnvironment?) {
        super.init(processingEnv)
        messager = processingEnv?.messager
        filer = processingEnv?.filer
        elementTool = processingEnv?.elementUtils
        typeTool = processingEnv?.typeUtils

        // 只有接受到 App壳 传递过来的书籍，才能证明我们的 APT环境搭建完成

        // 只有接受到 App壳 传递过来的书籍，才能证明我们的 APT环境搭建完成
        options = processingEnv?.options?.get(ProcessorConfig.OPTIONS)
        aptPackage = processingEnv?.options?.get(ProcessorConfig.APT_PACKAGE)

        messager!!.printMessage(
            Diagnostic.Kind.ERROR,
            ">>>>>>>>>>>>>>>>>>>>>> options:$options"
        )
        messager!!.printMessage(
            Diagnostic.Kind.ERROR,
            ">>>>>>>>>>>>>>>>>>>>>> aptPackage:$aptPackage"
        )
        if (options != null && aptPackage != null) {
            messager!!.printMessage(Diagnostic.Kind.NOTE, "APT 环境搭建完成....")
        } else {
            messager!!.printMessage(
                Diagnostic.Kind.ERROR,
                "APT 环境有问题，请检查 options 与 aptPackage 为null..."
            )
        }
    }


    override fun process(
        annotations: MutableSet<out TypeElement>?,
        roundEnv: RoundEnvironment?
    ): Boolean {

        annotations?.let {
            messager?.printMessage(Diagnostic.Kind.NOTE, ">>>>>>>>>>>>>>>>>>")
            val elements = roundEnv?.getElementsAnnotatedWith(ARouter::class.java)

            val activityType = elementTool?.getTypeElement(ProcessorConfig.ACTIVITY_PACKAGE)
            val activityMirror = activityType?.asType()

            elements?.let {
                for (element in elements) {
//                    /*  val methodSpec = MethodSpec.methodBuilder("main")
//                          .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
//                          .returns(Void.TYPE)
//                          .addParameter(Array<String>::class.java, "args")
//                          .addStatement("\$T.out.println(\$S)", System::class.java, "Hello, JavaPoet!")
//                          .build()
//
//                      val typeSpec = TypeSpec.classBuilder("HelloWorld")
//                          .addMethod(methodSpec)
//                          .addModifiers(Modifier.PUBLIC)
//                          .build()
//
//                      val javaFile = JavaFile.builder("com.example.test", typeSpec).build()
//                      javaFile.writeTo(filer)*/
//
//                    val finalClassName = element.simpleName.toString() + "ARoute"
//                    val aRouter = element.getAnnotation(ARouter::class.java)
//
//                    val packageName = elementTool?.getPackageOf(element)?.qualifiedName.toString()
//
//                    val methodSpec = MethodSpec.methodBuilder("findTargetClass")
//                        .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
//                        .returns(Class::class.java)
//                        .addParameter(String::class.java, "path")
//                        .addStatement(
//                            "return path.equals(\$S) ? \$T.class : null",
//                            aRouter.path,
//                            ClassName.get(element as TypeElement)
//                        )
//                        .build()
//
//                    val typeSpec = TypeSpec.classBuilder(finalClassName)
//                        .addMethod(methodSpec)
//                        .addModifiers(Modifier.PUBLIC)
//                        .build()
//
//                    val javaFile = JavaFile.builder(packageName, typeSpec).build()
//                    javaFile.writeTo(filer)

                    // 获取简单类名，例如：MainActivity
//                    val className = element.simpleName.toString()

                    // 拿到注解
                    val aRouter = element.getAnnotation(ARouter::class.java)

                    // TODO  一系列的检查工作
                    val routerBean = RouterBean.Builder()
                        .addGroup(aRouter.group)
                        .addPath(aRouter.path)
                        .addElement(element)
                        .build()

                    val elementMirror = element.asType()
                    if (typeTool?.isSubtype(elementMirror, activityMirror)!!) {
                        routerBean.typeEnum = (RouterBean.TypeEnum.ACTIVITY)
                    } else {
                        throw RuntimeException("@ARouter注解目前仅限用于Activity类之上")
                    }

                    if (checkRouterPath(routerBean)) {
                        var routerBeans = mAllPathMap[routerBean.group];
                        if (ProcessorUtils.isEmpty(routerBeans)) {
                            routerBeans = arrayListOf()
                            routerBeans.add(routerBean)
                            mAllPathMap[routerBean.group] = routerBeans
                        } else {
                            routerBeans?.add(routerBean)
                        }
                    } else { // ERROR 编译期发生异常
                        messager!!.printMessage(
                            Diagnostic.Kind.ERROR,
                            "@ARouter注解未按规范配置，如：/app/MainActivity"
                        )
                    }

                }
                // TODO end for  同学们注意：在循环外面了 （此循环结束后，仓库一 缓存一 就存好所有 Path值了）
                // mAllPathMap 里面有值了
                // 定义（生成类文件实现的接口） 有 Path Group
                val pathType =
                    elementTool?.getTypeElement(ProcessorConfig.AROUTER_API_PATH) // ARouterPath描述

                val groupType =
                    elementTool?.getTypeElement(ProcessorConfig.AROUTER_API_GROUP) // ARouterGroup描述


                // TODO 第一大步：系列PATH
                try {
                    createPathFile(pathType) // 生成 Path类
                } catch (e: IOException) {
                    e.printStackTrace()
                    messager!!.printMessage(Diagnostic.Kind.NOTE, "在生成PATH模板时，异常了 e:" + e.message)
                }


                // TODO 第二大步：组头（带头大哥）
                try {
                    createGroupFile(groupType, pathType)
                } catch (e: IOException) {
                    e.printStackTrace()
                    messager!!.printMessage(Diagnostic.Kind.NOTE, "在生成GROUP模板时，异常了 e:" + e.message)
                }

            }
        }


        return false
    }

    private fun createGroupFile(groupType: TypeElement?, pathType: TypeElement?) {


        if (ProcessorUtils.isEmpty(mAllPathMap) || ProcessorUtils.isEmpty(mAllGroupMap)) return
        // 返回值 这一段 Map<String, Class<? extends ARouterPath>>
        val methodReturn = ParameterizedTypeName.get(
            ClassName.get(Map::class.java),//Map
            ClassName.get(String::class.java),
            ParameterizedTypeName.get(
                ClassName.get(Class::class.java),
                // ? extends ARouterPath
                WildcardTypeName.subtypeOf(
                    ClassName.get(pathType)
                )
                // WildcardTypeName.supertypeOf() 做实验 ? super
            )
        )  // 最终的：Map<String, Class<? extends ARouterPath>>

        // 1.方法 public Map<String, Class<? extends ARouterPath>> getGroupMap() {
        val methodBuilder = MethodSpec.methodBuilder(ProcessorConfig.GROUP_METHOD_NAME)
            .addAnnotation(Override::class.java)
            .addModifiers(Modifier.PUBLIC)
            .returns(methodReturn)
        // Map<String, Class<? extends ARouterPath>> groupMap = new HashMap<>();
        methodBuilder.addStatement(
            "\$T<\$T , \$T> \$N = new \$T<>()",
            ClassName.get(Map::class.java),
            ClassName.get(String::class.java),
            ParameterizedTypeName.get(
                ClassName.get(Class::class.java),
                WildcardTypeName.subtypeOf(ClassName.get(pathType))
            ),
            ProcessorConfig.GROUP_VAR1,
            ClassName.get(HashMap::class.java)
        )
        //  groupMap.put("personal", ARouter$$Path$$personal.class);
        mAllGroupMap.forEach {
            methodBuilder.addStatement(
                "\$N.put(\$S ,\$T.class)",
                ProcessorConfig.GROUP_VAR1, // groupMap.put
                it.key, // order, personal ,app
                ClassName.get(aptPackage, it.value)
            )
        }
        // return groupMap;
        methodBuilder.addStatement("return \$N", ProcessorConfig.GROUP_VAR1)

        // 最终生成的类文件名 ARouter$$Group$$ + personal
        val finalClassName = ProcessorConfig.GROUP_FILE_NAME + options

        messager!!.printMessage(
            Diagnostic.Kind.NOTE, "APT生成路由组Group类文件：" +
                    aptPackage + "." + finalClassName
        )


        val packageBuilder = TypeSpec.classBuilder(finalClassName)
            .addSuperinterface(ClassName.get(groupType)) // 实现ARouterLoadPath接口  implements ARouterPath==pathType
            .addModifiers(Modifier.PUBLIC) // public修饰符
            .addMethod(methodBuilder.build()).build()

        JavaFile.builder(aptPackage, packageBuilder).build().writeTo(filer)


    }

    private fun createPathFile(pathType: TypeElement?) {
        if (mAllPathMap.isEmpty()) {
            return
        }
        // 任何的class类型，必须包装
        // Map<String, RouterBean>
        val methodReturn = ParameterizedTypeName.get(
            ClassName.get(Map::class.java)//Map
            , ClassName.get(String::class.java)//Map<String
            , ClassName.get(RouterBean::class.java) // Map<String, RouterBean>
        )

        // 遍历仓库 app,order,personal
        mAllPathMap.forEach {

            val methodBuilder = MethodSpec.methodBuilder(ProcessorConfig.PATH_METHOD_NAME)
                .addAnnotation(Override::class.java)
                .addModifiers(Modifier.PUBLIC)
                .returns(methodReturn)
            // Map<String, RouterBean> pathMap = new HashMap<>(); // $N == 变量 为什么是这个，因为变量有引用 所以是$N
            methodBuilder.addStatement(
                "\$T<\$T , \$T>  \$N =new \$T<>()",
                ClassName.get(Map::class.java),
                ClassName.get(String::class.java),
                ClassName.get(RouterBean::class.java),
                ProcessorConfig.PATH_VAR1,
                ClassName.get(HashMap::class.java)
            )


            // 必须要循环，因为有多个
            // pathMap.put("/app/MainActivity", RouterBean.create(RouterBean.TypeEnum.ACTIVITY, MainActivity.class, "/app/MainActivity", "app"));
            val pathList: List<RouterBean> = it.value

            pathList.forEach { value ->
                methodBuilder.addStatement(
                    "\$N.put(\$S, \$T.create(\$T.\$L, \$T.class, \$S, \$S))",
                    ProcessorConfig.PATH_VAR1,
                    value.path,
                    ClassName.get(RouterBean::class.java),
                    ClassName.get(RouterBean.TypeEnum::class.java),
                    value.typeEnum,
                    ClassName.get(value.element as TypeElement),
                    value.path,
                    value.group
                )
            }



            methodBuilder.addStatement("return \$N", ProcessorConfig.PATH_VAR1)
            // TODO 注意：不能像以前一样，1.方法，2.类  3.包， 因为这里面有implements ，所以 方法和类要合为一体生成才行，这是特殊情况
            // 最终生成的类文件名  ARouter$$Path$$personal
            val finalClassName = ProcessorConfig.PATH_FILE_NAME + it.key // key ==  app
            messager!!.printMessage(
                Diagnostic.Kind.NOTE, "APT生成路由Path类文件：" +
                        aptPackage + "." + finalClassName
            )
            val packageBuilder = TypeSpec.classBuilder(finalClassName)
                .addSuperinterface(ClassName.get(pathType)) // 实现ARouterLoadPath接口  implements ARouterPath==pathType
                .addModifiers(Modifier.PUBLIC) // public修饰符
                .addMethod(methodBuilder.build()).build()

            JavaFile.builder(aptPackage, packageBuilder).build() // JavaFile构建完成
                .writeTo(filer)// 文件生成器开始生成类文件

            // 仓库二 缓存二  非常重要一步，注意：PATH 路径文件生成出来了，才能赋值路由组mAllGroupMap
            mAllGroupMap[it.key!!] = finalClassName
        }
    }


    private fun checkRouterPath(bean: RouterBean): Boolean {
        val group: String? = bean.group
        val path: String? = bean.path// "/app/MainActivity"

        // 校验
        // @ARouter注解中的path值，必须要以 / 开头（模仿阿里Arouter规范）
        if (ProcessorUtils.isEmpty(path) || !path!!.startsWith("/")) {
            messager!!.printMessage(Diagnostic.Kind.ERROR, "@ARouter注解中的path值，必须要以 / 开头")
            return false
        }

        // 比如开发者代码为：path = "/MainActivity"，最后一个 / 符号必然在字符串第1位
        if (path.lastIndexOf("/") == 0) {
            // 架构师定义规范，让开发者遵循
            messager!!.printMessage(Diagnostic.Kind.ERROR, "@ARouter注解未按规范配置，如：/app/MainActivity")
            return false
        }

        // 从第一个 / 到第二个 / 中间截取，如：/app/MainActivity 截取出 app,order,personal 作为group
        val finalGroup = path.substring(1, path.indexOf("/", 1))

        // app,order,personal == options
        // @ARouter注解中的group有赋值情况
        if (!ProcessorUtils.isEmpty(group) && group != options) {
            // 架构师定义规范，让开发者遵循
            messager!!.printMessage(Diagnostic.Kind.ERROR, "@ARouter注解中的group值必须和子模块名一致！")
            return false
        } else {
            bean.group = finalGroup
        }
        return true
    }


}